//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/******************************************************************************
**
**  COPYRIGHT (C) 2004 Intel Corporation.
**
**  This software as well as the software described in it is furnished under
**  license and may only be used or copied in accordance with the terms of the
**  license. The information in this file is furnished for informational use
**  only, is subject to change without notice, and should not be construed as
**  a commitment by Intel Corporation. Intel Corporation assumes no
**  responsibility or liability for any errors or inaccuracies that may appear
**  in this document or any software that may be provided in association with
**  this document. 
**  Except as permitted by such license, no part of this document may be 
**  reproduced, stored in a retrieval system, or transmitted in any form or by
**  any means without the express written consent of Intel Corporation. 
**
**  FILENAME:  xllp_lcd_cpu.c
**
**  PURPOSE:   common source file for XLLP LCD controller. 
**
*******************************************************************************/

#include "xllp_defs.h"

#include "xllp_ost.h"
#include "xllp_lcd.h"
#include "xllp_lcd_cpu.h"
#include "xllp_gpio.h"

#define SPINSLEEP(millisecs) XllpOstDelayMilliSeconds(pOSTRegs, millisecs)
XLLP_OST_T *pOSTRegs = NULL;

void XllpLCDLoadPalette(P_XLLP_LCD_T pXllpLCD)
{   
    XLLP_LCD_REGISTERS_T* pLCDRegs;
    LCD_FRAME_DESCRIPTOR *fdVirtTemp;
    XLLP_UINT32_T fdPhysTemp;

    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC;

    // Reconfigure the second frame descriptor so that when loaded,
    // this descriptor loops to itself.
    pXllpLCD->frameDescriptorCh0fd2->FDADR = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL);

    // Reconfigure the palette frame descriptor so that it loads the second frame descriptor
    pXllpLCD->frameDescriptorPalette->FDADR = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL);
    pXllpLCD->frameDescriptorPalette->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_PALETTE_BUFFER_BASE_PHYSICAL); 
    pXllpLCD->frameDescriptorPalette->FIDR  = XLLP_FIDR_FRAMEID(0);


    if ( (pLCDRegs->ovl1c1 & XLLP_OVL1C1_O1EN) || (pLCDRegs->ovl2c1 & XLLP_OVL2C1_O2EN)) 
    {
        // Overlays are enabled
        pXllpLCD->frameDescriptorPalette->LDCMD = XLLP_LDCMD_LEN(pXllpLCD->PaletteSize << 1) | XLLP_LDCMD_PAL;
    } else
    {
        // Overlays are disabled
        pXllpLCD->frameDescriptorPalette->LDCMD = XLLP_LDCMD_LEN(pXllpLCD->PaletteSize) | XLLP_LDCMD_PAL;
    }

    //pXllpLCD->frameDescriptorPalette->PHYSADDR = XLLP_FDADR_DESCADDR(pXllpLCD->_PALETTE_FRAME_DESCRIPTOR_BASE_PHYSICAL);

    // Insert the palette descriptor into the descriptor chain to load the palette.
    // When this load completes, fd2 is automatically loaded next in the chain.  
    // fd2 now loops to itself and continues to load frame data.
    pXllpLCD->frameDescriptorCh0fd1->FDADR = XLLP_FDADR_DESCADDR(pXllpLCD->_PALETTE_FRAME_DESCRIPTOR_BASE_PHYSICAL);    

    // swap frame descriptor pointers so that this operation is reversed the next time through
    fdVirtTemp = pXllpLCD->frameDescriptorCh0fd1;
    fdPhysTemp = pXllpLCD->_DMA_CHANNEL_0_FRAME_DESCRIPTOR_BASE_PHYSICAL;
    pXllpLCD->frameDescriptorCh0fd1 = pXllpLCD->frameDescriptorCh0fd2;
    pXllpLCD->_DMA_CHANNEL_0_FRAME_DESCRIPTOR_BASE_PHYSICAL = pXllpLCD->_DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL;
    pXllpLCD->frameDescriptorCh0fd2 = fdVirtTemp;
    pXllpLCD->_DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL = fdPhysTemp;
}

void LCDSuspendController(P_XLLP_LCD_T pXllpLCD, int SuspendType)
{
    XLLP_LCD_REGISTERS_T* pLCDRegs;

    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC;

    switch(SuspendType)
    {
    case Suspend_Graceful:
        // Initiate power down sequence
        pLCDRegs->lccr0 |= XLLP_LCCR0_DIS;

        // Wait for LDD bit to get set once the last DMA transfer has completed
        while(!(pLCDRegs->lcsr0 & XLLP_LCSR0_LDD));

        // Clear the sticky LDD bit
        pLCDRegs->lcsr0 |= XLLP_LCSR0_LDD;
        break;
    case Suspend_Immediate:
        pLCDRegs->lccr0 &= ~XLLP_LCCR0_ENB;
        break;
    default :
        break;
    }
    
    return;
}

void XllpLCDSetDisplayPage(P_XLLP_LCD_T pXllpLCD, int page)
{
    // Set the physical address of the frame buffer for all three frame descriptors
    // Make sure that you've initialized FrameBufferSize before calling this function either manually
    // or through a call to XllpLCDInit().
    pXllpLCD->CurrentPage = page;
    pXllpLCD->frameDescriptorCh0fd1->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_FRAME_BUFFER_BASE_PHYSICAL + pXllpLCD->CurrentPage*pXllpLCD->FrameBufferSize);
    pXllpLCD->frameDescriptorCh0fd2->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_FRAME_BUFFER_BASE_PHYSICAL + pXllpLCD->CurrentPage*pXllpLCD->FrameBufferSize);
}

void LCDInitController(P_XLLP_LCD_T pXllpLCD)
{
    int BPP = 0;
    XLLP_LCD_REGISTERS_T* pLCDRegs;
    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC;

    if (pOSTRegs != (XLLP_OST_T *)pXllpLCD->OST)
        pOSTRegs = (XLLP_OST_T *)pXllpLCD->OST;

    // Determine the frame buffer size for the DMA transfer length.
    // Scale the size based on the bpp of the frame buffer to determine
    // an actual size in bytes
    pXllpLCD->FrameBufferSize = pXllpLCD->FrameBufferWidth * pXllpLCD->FrameBufferHeight;
    switch (pXllpLCD->BPP)
    {
        case BPP_1:
            pXllpLCD->FrameBufferSize >>= 3;
            pXllpLCD->PaletteSize = 8;
            break;
        case BPP_2:
            pXllpLCD->FrameBufferSize >>= 2;
            pXllpLCD->PaletteSize = 8;
            break;
        case BPP_4:
            pXllpLCD->FrameBufferSize >>= 1;
            pXllpLCD->PaletteSize = 32;
            break;
        case BPP_8:
            pXllpLCD->PaletteSize = 512;
            break;
        case BPP_16:
            pXllpLCD->FrameBufferSize <<= 1;
            break;
        case BPP_18:        /* Fall through */
        case BPP_18_PACKED:
        case BPP_19:
        case BPP_19_PACKED:
        case BPP_24:
        case BPP_25:
            pXllpLCD->FrameBufferSize <<= 2;
            break;
        default:
            break;
    }

    // Configure the general purpose frame descriptors
    // Set the physical address of the frame descriptor
    pXllpLCD->frameDescriptorCh0fd1->FDADR = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_0_FRAME_DESCRIPTOR_BASE_PHYSICAL);

    // Set the physical address of the frame buffer
    pXllpLCD->frameDescriptorCh0fd1->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_FRAME_BUFFER_BASE_PHYSICAL + pXllpLCD->CurrentPage*pXllpLCD->FrameBufferSize);

    // Clear the frame ID
    pXllpLCD->frameDescriptorCh0fd1->FIDR  = XLLP_FIDR_FRAMEID(0);

    // Set the DMA transfer length to the size of the frame buffer
    pXllpLCD->frameDescriptorCh0fd1->LDCMD = XLLP_LDCMD_LEN(pXllpLCD->FrameBufferSize);

    // : xllp hardcodes certain things... like EOF interrupt.  Wedging in here...
    //
    pXllpLCD->frameDescriptorCh0fd1->LDCMD |= 0x00200000;           // enable EOF int


    // frameDescriptorCh0fd2 is used only if a palette load is performed.
    // Set the physical address of the frame descriptor
    pXllpLCD->frameDescriptorCh0fd2->FDADR = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL);

    // Set the physical address of the frame buffer
    pXllpLCD->frameDescriptorCh0fd2->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_FRAME_BUFFER_BASE_PHYSICAL + pXllpLCD->CurrentPage*pXllpLCD->FrameBufferSize);

    // Clear the frame ID
    pXllpLCD->frameDescriptorCh0fd2->FIDR  = XLLP_FIDR_FRAMEID(0);

    // Set the DMA transfer length to the size of the frame buffer
    pXllpLCD->frameDescriptorCh0fd2->LDCMD = XLLP_LDCMD_LEN(pXllpLCD->FrameBufferSize);
    
    // FBR0 is cleared and is not used.
    pLCDRegs->fbr0 = 0;

    // Load the contents of FDADR0 with the physical address of this frame descriptor
    pLCDRegs->fdadr0 = XLLP_FDADR_DESCADDR(pXllpLCD->frameDescriptorCh0fd1->FDADR);
            
    // Convert the bpp setting into a value that the LCD controller understands.
    switch(pXllpLCD->BPP)
    {
        case BPP_1:
            BPP = 0;
            break;
        case BPP_2:
            BPP = 1;
            break;
        case BPP_4:
            BPP = 2;
            break;
        case BPP_8:
            BPP = 3;
            break;
        case BPP_16:
            BPP = 4;
            break;
        case BPP_18:
            BPP = 5;
            break;
        case BPP_18_PACKED:
            BPP = 6;
            break;
        case BPP_19:
            BPP = 7;
            break;
        case BPP_19_PACKED:
            BPP = 8;
            break;
        case BPP_24:
            BPP = 9;
            break;
        case BPP_25:
            BPP = 10;
            break;
        default:
        {
            BPP = 0;
            break;
        }
    }
    
    //lccr0-4 will be configured by platform-specific code. 
    pLCDRegs->lccr0 = 0;
    pLCDRegs->lccr1 = 0;
    pLCDRegs->lccr2 = 0;
    pLCDRegs->lccr3 = XLLP_LCCR3_BPP(BPP);
    pLCDRegs->lccr4 = XLLP_LCCR4_REOFM0|XLLP_LCCR4_REOFM1|XLLP_LCCR4_REOFM2|XLLP_LCCR4_REOFM3|XLLP_LCCR4_REOFM4|XLLP_LCCR4_REOFM5|XLLP_LCCR4_REOFM6;
    pLCDRegs->lccr5 = (XLLP_LCCR5_SOFM1|XLLP_LCCR5_SOFM2|XLLP_LCCR5_SOFM3|XLLP_LCCR5_SOFM4|XLLP_LCCR5_SOFM5|XLLP_LCCR5_SOFM6|
                        XLLP_LCCR5_EOFM1|XLLP_LCCR5_EOFM2|XLLP_LCCR5_EOFM3|XLLP_LCCR5_EOFM4|XLLP_LCCR5_EOFM5|XLLP_LCCR5_EOFM6|
                        XLLP_LCCR5_BSM1 |XLLP_LCCR5_BSM2 |XLLP_LCCR5_BSM3 |XLLP_LCCR5_BSM4 |XLLP_LCCR5_BSM5 |XLLP_LCCR5_BSM6 |
                        XLLP_LCCR5_IUM1 |XLLP_LCCR5_IUM2 |XLLP_LCCR5_IUM3 |XLLP_LCCR5_IUM4 |XLLP_LCCR5_IUM5 |XLLP_LCCR5_IUM6 );
    pLCDRegs->lccr6 = 0;

    // It is driver's responsibility now?
    // Enable the LCD clocks
//  If need be use proper serialization object for CKEN
    XllpClockEnable((XLLP_CLKMGR_T*)(pXllpLCD->CLKMan), XLLP_CLK_LCD, XLLP_TRUE);
//  Release serialization object
    
    return;
}


void LCDClearStatusReg(P_XLLP_LCD_T pXllpLCD)
{
    XLLP_LCD_REGISTERS_T* pLCDRegs;
    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC;

    // Clear the status registers by writing 1's to each bit.
    pLCDRegs->lcsr0 =   ( XLLP_LCSR0_LDD | XLLP_LCSR0_SOF0  | XLLP_LCSR0_BER | XLLP_LCSR0_ABC  | XLLP_LCSR0_IU0 |
                          XLLP_LCSR0_IU1 | XLLP_LCSR0_OU    | XLLP_LCSR0_QD  | XLLP_LCSR0_EOF0 | XLLP_LCSR0_BS0 | 
                          XLLP_LCSR0_SINT| XLLP_LCSR0_RD_ST | XLLP_LCSR0_CMD_INTR |
                          XLLP_LCSR0_REOF0| XLLP_LCSR0_REOF1|XLLP_LCSR0_REOF2|XLLP_LCSR0_REOF3 |XLLP_LCSR0_REOF4|XLLP_LCSR0_REOF5 |XLLP_LCSR0_REOF6 );

    pLCDRegs->lcsr1 =   ( XLLP_LCSR1_SOF1| XLLP_LCSR1_SOF2 | XLLP_LCSR1_SOF3| XLLP_LCSR1_SOF4| XLLP_LCSR1_SOF5  | XLLP_LCSR1_SOF6   |
                          XLLP_LCSR1_EOF1| XLLP_LCSR1_EOF2 | XLLP_LCSR1_EOF3| XLLP_LCSR1_EOF4| XLLP_LCSR1_EOF5  | XLLP_LCSR1_EOF6   |
                          XLLP_LCSR1_BS1 | XLLP_LCSR1_BS2  | XLLP_LCSR1_BS3 | XLLP_LCSR1_BS4 | XLLP_LCSR1_BS5   | XLLP_LCSR1_BS6    |
                                           XLLP_LCSR1_IU2  | XLLP_LCSR1_IU3 | XLLP_LCSR1_IU4 | XLLP_LCSR1_IU5   | XLLP_LCSR1_IU6 );

    return;
}

void LCDEnableController(P_XLLP_LCD_T pXllpLCD)
{
    XLLP_LCD_REGISTERS_T* pLCDRegs;

    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC;

    pLCDRegs->lccr0 |= XLLP_LCCR0_ENB;
}

typedef enum
{
     WFS_ALL,
     WFS_ANY
} WFS_TYPE;

static int WaitForState(XLLP_VUINT32_T (*pStatusReg), unsigned int mask, WFS_TYPE wait_type, unsigned int max_ms)
// Wait a max # milliseconds for a register to match a given state (any or all bits)
// Returns number of milliseconds of max_ms remaining (ie. >= 0 = got match, < 0 = no match) 
    {
    int matched = 0;
    int sleep_quantum = (max_ms/100); // wait 1/100 and see
    int sleep_time_left = max_ms;

    if (sleep_quantum == 0)
        sleep_quantum = 1;
    while (1)
    {
        matched = (wait_type == WFS_ALL) ? ((*pStatusReg & mask) == mask)
                                         : ((*pStatusReg & mask) != 0);
        if (matched)
        {
            if (sleep_time_left < 0)
                sleep_time_left = 0;
            break;
        }
        if (sleep_time_left < 0)
            break;
        SPINSLEEP(sleep_quantum);
        sleep_time_left -= sleep_quantum;
    }
    return(sleep_time_left);
} // WaitForState


XLLP_STATUS_T XllpLCD_Overlay2_Enable(P_XLLP_LCD_T pXllpLCD, P_XLLP_OVERLAY_T pXllpOverlay)
{
    XLLP_STATUS_T status = 0;
    XLLP_LCD_REGISTERS_T* pLCDRegs;
    // unsigned int StatMask = 0; 

    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC; 

    if ((pLCDRegs->ovl2c1) & XLLP_OVL2C1_O2EN)
    {
        return(status);
    }

    // Change baseframe pixel format to reduce memory bus bandwidth
    if (pXllpOverlay->DegradeBaseFrame) 
    {
        XllpLCDSuspend(pXllpLCD, Suspend_Graceful);
        pXllpOverlay->TmpBPP = pXllpLCD->BPP;
        pXllpLCD->BPP = BPP_1;
        XllpLCDResume(pXllpLCD);
    }

    // Setup frame descriptors
    OverlayDMALength(pXllpOverlay);
    // CH2
    pXllpLCD->frameDescriptorCh2_YCbCr_Y->FDADR     = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_2_Y_FRAME_DESCRIPTOR_BASE_PHYSICAL);                        // next = self
    pXllpLCD->frameDescriptorCh2_YCbCr_Y->FSADR     = XLLP_FSADR_SRCADDR(pXllpLCD->_OVERLAY2_Y_CHANNEL_BASE_PHYSICAL);                // frame buffer
    pXllpLCD->frameDescriptorCh2_YCbCr_Y->FIDR      = XLLP_FIDR_FRAMEID(0);
    pXllpLCD->frameDescriptorCh2_YCbCr_Y->LDCMD     = XLLP_LDCMD_LEN(pXllpOverlay->ch2_size);
    // CH3
    pXllpLCD->frameDescriptorCh3_YCbCr_Cb->FDADR    = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_3_Cb_FRAME_DESCRIPTOR_BASE_PHYSICAL);                       // next = self
    pXllpLCD->frameDescriptorCh3_YCbCr_Cb->FSADR    = XLLP_FSADR_SRCADDR(pXllpLCD->_OVERLAY2_Cb_CHANNEL_BASE_PHYSICAL);               // frame buffer
    pXllpLCD->frameDescriptorCh3_YCbCr_Cb->FIDR     = XLLP_FIDR_FRAMEID(0);
    pXllpLCD->frameDescriptorCh3_YCbCr_Cb->LDCMD    = XLLP_LDCMD_LEN(pXllpOverlay->ch3_size);
    // CH4
    pXllpLCD->frameDescriptorCh4_YCbCr_Cr->FDADR    = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_4_Cr_FRAME_DESCRIPTOR_BASE_PHYSICAL);                       // next = self
    pXllpLCD->frameDescriptorCh4_YCbCr_Cr->FSADR    = XLLP_FSADR_SRCADDR(pXllpLCD->_OVERLAY2_Cr_CHANNEL_BASE_PHYSICAL);               // frame buffer
    pXllpLCD->frameDescriptorCh4_YCbCr_Cr->FIDR     = XLLP_FIDR_FRAMEID(0);
    pXllpLCD->frameDescriptorCh4_YCbCr_Cr->LDCMD    = XLLP_LDCMD_LEN(pXllpOverlay->ch4_size);
    
    // FBRx is cleared and is not used.
    pLCDRegs->fbr2 = 0;
    pLCDRegs->fbr3 = 0;
    pLCDRegs->fbr4 = 0;

    pLCDRegs->fdadr2 = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_2_Y_FRAME_DESCRIPTOR_BASE_PHYSICAL);
    if (pXllpOverlay->Format != FORMAT_RGB)
    {
        pXllpOverlay->OverlayBPP = 0x2; // This is the reset value. Let it be.
        if (pXllpOverlay->Format != FORMAT_PACKED_444)
        {
            pLCDRegs->fdadr3 = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_3_Cb_FRAME_DESCRIPTOR_BASE_PHYSICAL);
            pLCDRegs->fdadr4 = XLLP_FDADR_DESCADDR(pXllpLCD->_DMA_CHANNEL_4_Cr_FRAME_DESCRIPTOR_BASE_PHYSICAL);
        }
    }

    pLCDRegs->ovl2c2 = XLLP_OVL2C2_FOR(pXllpOverlay->Format) | XLLP_OVL2C2_O2YPOS(pXllpOverlay->Y_Position) | XLLP_OVL2C2_O2XPOS(pXllpOverlay->X_Position);
    pLCDRegs->ovl2c1 = XLLP_OVL2C1_O2EN | XLLP_OVL2C1_BPP2(pXllpOverlay->OverlayBPP) | XLLP_OVL2C1_LPO2(pXllpOverlay->OverlayHeight-1) | XLLP_OVL2C1_PPL2(pXllpOverlay->OverlayWidth-1);
    
    return(status);
} // XllpLCD_Overlay2_Enable

void XllpLCD_Overlay2_Disable(P_XLLP_LCD_T pXllpLCD, P_XLLP_OVERLAY_T pXllpOverlay)
{
    XLLP_LCD_REGISTERS_T* pLCDRegs;
    unsigned int OVL2C1;
    unsigned int OVL2C2;
    unsigned int OL2Format;
    unsigned int StatMask = 0; 

    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC; 

    OVL2C1 = pLCDRegs->ovl2c1;
    if (!(OVL2C1 & XLLP_OVL2C1_O2EN))
    {
        return;
    }
    
    // Get current overlay format 
    OVL2C2 = pLCDRegs->ovl2c2;
    OL2Format = (OVL2C2 >> 20) & 7;
    StatMask = XLLP_LCSR1_BS2;
    if (OL2Format != FORMAT_RGB)
        StatMask |= (XLLP_LCSR1_BS3 | XLLP_LCSR1_BS4);
        
    pLCDRegs->lcsr1 = StatMask;
    pLCDRegs->ovl2c1 &= ~XLLP_OVL2C1_O2EN; // Clear the enable bit

    // Wait for DMA branch taken status (BSx bits to be set).
    // This is per HW Eng. workaround described in Tibet sighting 49219.
    // Note that testing indicates the loading of FBRx avoids the hardware problem.
    // Not loading FBRx and just waiting for 50ms does not avoid the problem.

    if ((OL2Format != FORMAT_RGB)&&(OL2Format != FORMAT_PACKED_444))
    {
        pLCDRegs->fbr3 = XLLP_FDADR_DESCADDR(pXllpLCD->frameDescriptorCh3_YCbCr_Cb->FDADR) | XLLP_FBR_BRA | XLLP_FBR_BINT;
        pLCDRegs->fbr4 = XLLP_FDADR_DESCADDR(pXllpLCD->frameDescriptorCh4_YCbCr_Cr->FDADR) | XLLP_FBR_BRA | XLLP_FBR_BINT;
    }
    pLCDRegs->fbr2 = XLLP_FDADR_DESCADDR(pXllpLCD->frameDescriptorCh2_YCbCr_Y->FDADR) | (XLLP_FBR_BRA | XLLP_FBR_BINT);

    WaitForState(&(pLCDRegs->lcsr1),StatMask,WFS_ALL,50);
    
    if (pXllpOverlay->DegradeBaseFrame) 
    {
        // Restore BaseFrame Pixel Format
        XllpLCDSuspend(pXllpLCD, Suspend_Graceful);
        pXllpLCD->PixelDataFormat = PDFOR_00;
        pXllpLCD->BPP = pXllpOverlay->TmpBPP;
        XllpLCDResume(pXllpLCD);
    }

    return;
} // XllpLCD_Overlay2_Disable


XLLP_STATUS_T XllpLCD_Overlay2_DynChange(P_XLLP_LCD_T pXllpLCD, P_XLLP_OVERLAY_T pXllpOverlay, XLLP_UINT32_T changeFlag)
{
    XLLP_LCD_REGISTERS_T* pLCDRegs;
    pLCDRegs = (XLLP_LCD_REGISTERS_T*) pXllpLCD->LCDC;
    
    if (!(pLCDRegs->ovl2c1 & XLLP_OVL2C1_O2EN)) 
        return XLLP_STATUS_INVALID_STATE;
        
    if (changeFlag & Change_Size_Position) 
    {
        pLCDRegs->ovl2c2 = (XLLP_OVL2C2_FOR(pXllpOverlay->Format) | XLLP_OVL2C2_O2YPOS(pXllpOverlay->Y_Position) | XLLP_OVL2C2_O2XPOS(pXllpOverlay->X_Position));
        pLCDRegs->ovl2c1 = (XLLP_OVL2C1_O2EN | XLLP_OVL2C1_BPP2(pXllpOverlay->OverlayBPP) | XLLP_OVL2C1_LPO2(pXllpOverlay->OverlayHeight-1) | XLLP_OVL2C1_PPL2(pXllpOverlay->OverlayWidth-1));
    } 
    
    if (changeFlag & Change_FrameBuffer) 
    {
        pXllpLCD->frameDescriptorCh2_YCbCr_Y->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_OVERLAY2_Y_CHANNEL_BASE_PHYSICAL);                // frame buffer
        pXllpLCD->frameDescriptorCh3_YCbCr_Cb->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_OVERLAY2_Cb_CHANNEL_BASE_PHYSICAL);               // frame buffer
        pXllpLCD->frameDescriptorCh4_YCbCr_Cr->FSADR = XLLP_FSADR_SRCADDR(pXllpLCD->_OVERLAY2_Cr_CHANNEL_BASE_PHYSICAL);               // frame buffer
    }
    
    pLCDRegs->fbr2 = pXllpLCD->_DMA_CHANNEL_2_Y_FRAME_DESCRIPTOR_BASE_PHYSICAL | XLLP_FBR_BRA ;
    if ((pXllpOverlay->Format != FORMAT_RGB)&&(pXllpOverlay->Format != FORMAT_PACKED_444))
    {
        pLCDRegs->fbr3 = pXllpLCD->_DMA_CHANNEL_3_Cb_FRAME_DESCRIPTOR_BASE_PHYSICAL | XLLP_FBR_BRA ;
        pLCDRegs->fbr4 = pXllpLCD->_DMA_CHANNEL_4_Cr_FRAME_DESCRIPTOR_BASE_PHYSICAL | XLLP_FBR_BRA ;
        if (changeFlag & Change_Wait_For_Complete) 
        {
            // use thread-switching wait here.
            while(pLCDRegs->fbr3&XLLP_FBR_BRA);
            while(pLCDRegs->fbr4&XLLP_FBR_BRA);     
        }
    }
    if (changeFlag & Change_Wait_For_Complete) 
    {
        while(pLCDRegs->fbr2&XLLP_FBR_BRA);
    }
    
    return XLLP_STATUS_SUCCESS;
}

void OverlayDMALength(P_XLLP_OVERLAY_T pXllpOverlay)
{
    unsigned int width = pXllpOverlay->OverlayWidth;
    unsigned int pixels;    
    unsigned int DMALength = 0;

    // Determine the DMA transfer length
    // each DMA transfer length for YUV formatted data must be multiples of 32-bits and adjusted accordingly
    if (pXllpOverlay->Format == FORMAT_RGB)
    {
        switch(pXllpOverlay->OverlayBPP)
        {
            case O_BPP_4:   // pixels per line should be multiple of 8
                width = (width + 0x7) & (~0x7);
                pixels = pXllpOverlay->OverlayHeight * width;
                //pXllpOverlay->OverlayWidth = width;
                DMALength = pixels >> 1;
                break;
            case O_BPP_8:   // pixels per line should be multiple of 4
                width = (width + 0x3) & (~0x3);
                pixels = pXllpOverlay->OverlayHeight * width;
                //pXllpOverlay->OverlayWidth = width;
                DMALength = pixels;
                break;
            case O_BPP_16:  // pixels per line should be multiple of 2
                width = (width + 0x1) & (~0x1);
                pixels = pXllpOverlay->OverlayHeight * width;
                //pXllpOverlay->OverlayWidth = width;
                DMALength = pixels << 1;
                break;
            case O_BPP_18:  // no requirement for width
            case O_BPP_19:
            case O_BPP_24:
            case O_BPP_25:
                pixels = pXllpOverlay->OverlayHeight * width;
                DMALength = pixels << 2;
                break;
            case O_BPP_18_PACKED:
            case O_BPP_19_PACKED:
                width = (width + 0x7) & (~0x7);
                pixels = pXllpOverlay->OverlayHeight * width;
                //pXllpOverlay->OverlayWidth = width;
                DMALength = pixels << 2;
                break;

            default:
                break;
        }
        pXllpOverlay->ch2_size = DMALength;
        pXllpOverlay->ch3_size = 0;
        pXllpOverlay->ch4_size = 0;
    }
    else if (pXllpOverlay->Format == FORMAT_PACKED_444)
    {
        // One pixel uses 3 bytes.
        // pixels per line should be multiple of 4, to get word alignment.
        width = (width + 0x3) & (~0x3);
        pixels = pXllpOverlay->OverlayHeight * width;
        //pXllpOverlay->OverlayWidth = width;
        pXllpOverlay->ch2_size = (pixels << 2);
        pXllpOverlay->ch3_size = 0;
        pXllpOverlay->ch4_size = 0;
    } 
    else if (pXllpOverlay->Format == FORMAT_PLANAR_444) 
    {
        // One pixel uses 1 byte in every plane.
        // pixels per line should be multiple of 4, to get word alignment   
        width = (width + 0x3) & (~0x3);
        pixels = pXllpOverlay->OverlayHeight * width;
        //pXllpOverlay->OverlayWidth = width;
        DMALength = pixels;
        
        pXllpOverlay->ch2_size = DMALength;
        pXllpOverlay->ch3_size = DMALength;
        pXllpOverlay->ch4_size = DMALength;
    }
    else if (pXllpOverlay->Format == FORMAT_PLANAR_422)
    {                                                                           
        // Two pixels use 1 byte in Cb/Cr plane.
        // pixels per line should be multiple of 8, to get word alignment
        width = (width + 0x7) & (~0x7);
        pixels = pXllpOverlay->OverlayHeight * width;
        //pXllpOverlay->OverlayWidth = width;
        DMALength = pixels;
        pXllpOverlay->ch2_size = DMALength;

        DMALength = pixels >> 1;
        pXllpOverlay->ch3_size = DMALength;
        pXllpOverlay->ch4_size = DMALength;
    }
    else if (pXllpOverlay->Format == FORMAT_PLANAR_420)
    {
        // Four pixels use 1 byte in Cb/Cr plane ???
        // pixels per line should be multiple of 16, to get word alignment
        width = (pXllpOverlay->OverlayWidth + 0xf) & (~0xf);
        //pXllpOverlay->OverlayWidth = width;
        pixels = pXllpOverlay->OverlayHeight * width;
        DMALength = pixels;
        
        pXllpOverlay->ch2_size = DMALength;

        DMALength = pixels >> 2;
        pXllpOverlay->ch3_size = DMALength;
        pXllpOverlay->ch4_size = DMALength;
    }
    return;
}

// The macro to downsample RGB16 to RGB555
#define rgb16to15(n) (XLLP_UINT16_T)((n&0x1f)|((n&0xffc0)>>1))
            
XLLP_STATUS_T MiniLCDEncodeRGB565(P_XLLP_MINI_LCD_T pXllpMiniLCD, 
                                  XLLP_UINT32_T FrameSource, 
                                  P_XLLP_UINT32_T pByteCount,
                                  XLLP_UINT16_T width,
                                  XLLP_UINT16_T height)
{
    XLLP_UINT16_T lastValue, currentValue, count;
    XLLP_UINT32_T i, j, m, n;
    XLLP_UINT32_T quota;
    P_XLLP_UINT16_T s;
    P_XLLP_UINT16_T d;
    
    if ((FrameSource&1)||(pXllpMiniLCD->SRAM_virt&7)) return XLLP_STATUS_WRONG_PARAMETER;
    
    s = (P_XLLP_UINT16_T)FrameSource;
    d = (P_XLLP_UINT16_T)pXllpMiniLCD->SRAM_virt;
    m = n = 0;
    quota = pXllpMiniLCD->SRAMQuota - 1;
    for(i=0;i<height;i++) 
    {
        if (n>quota) return XLLP_STATUS_NO_RESOURCES;
        lastValue = d[n++] = rgb16to15(s[m]);
        m += 1;
        count = 0;
        for(j=1;j<width;j++) 
        {
            currentValue = rgb16to15(s[m]);
            if (currentValue == lastValue) 
            {
                count += 1;
                m += 1;
            } 
            else 
            {
                if (count>0) 
                {
                    if (n>quota) return XLLP_STATUS_NO_RESOURCES;
                    d[n++] = count; 
                }
                if (n>quota) return XLLP_STATUS_NO_RESOURCES;
                lastValue = d[n++] = currentValue;
                count = 0;
            }
        }
        if (n>quota) return XLLP_STATUS_NO_RESOURCES;       
        d[n++] = 0x9001;
    }
    if (n>quota) return XLLP_STATUS_NO_RESOURCES;   
    d[n++] = 0xa001;
    // Padding for multiple of 64-bit in length 
    while (n & 7)
    {
        if (n>quota) return XLLP_STATUS_NO_RESOURCES;
        d[n++] = 0xf001;
    }
    *pByteCount = n;            
    return XLLP_STATUS_SUCCESS;
}


XLLP_STATUS_T XllpLCDInit(P_XLLP_LCD_T pXllpLCD)
{
	return pXllpLCD->panel_proc.init(pXllpLCD);
}

void XllpLCDSuspend(P_XLLP_LCD_T pXllpLCD, int SuspendType)
{
	pXllpLCD->panel_proc.suspend(pXllpLCD, SuspendType);
}

void XllpLCDResume(P_XLLP_LCD_T pXllpLCD)
{
	pXllpLCD->panel_proc.resume(pXllpLCD);
}

void XllpLCDAdjustPCD(P_XLLP_LCD_T pXllpLCD, XLLP_UINT32_T hss)
{
	pXllpLCD->panel_proc.adjust_pcd(pXllpLCD, hss);
}

void XllpMiniLCDEnable(P_XLLP_MINI_LCD_T pXllpMiniLCD)
{
	pXllpMiniLCD->panel_proc.mini_lcd_enable(pXllpMiniLCD);
}

void XllpMiniLCDDisable(P_XLLP_MINI_LCD_T pXllpMiniLCD)
{
	pXllpMiniLCD->panel_proc.mini_lcd_disable(pXllpMiniLCD);
}

void XllpMiniLCDEncode(P_XLLP_MINI_LCD_T pXllpMiniLCD, 
					   XLLP_UINT32_T FrameSource, 
					   P_XLLP_UINT32_T pByteCount)
{
	pXllpMiniLCD->panel_proc.mini_lcd_encode(pXllpMiniLCD, FrameSource, pByteCount);
}



static XLLP_UINT32_T mfp_list []={
	XLLP_MFP_L_DD_8_OFFSET,    
	XLLP_MFP_L_DD_9_OFFSET,   
	XLLP_MFP_L_DD_10_OFFSET,   
	XLLP_MFP_L_DD_11_OFFSET,   
	XLLP_MFP_L_DD_12_OFFSET,       
	XLLP_MFP_L_DD_13_OFFSET,       
	XLLP_MFP_L_DD_14_OFFSET,       
	XLLP_MFP_L_DD_15_OFFSET,       
	XLLP_MFP_L_DD_16_OFFSET,       
	XLLP_MFP_L_DD_17_OFFSET,       
	XLLP_MFP_L_DD_0_OFFSET,   
	XLLP_MFP_L_DD_1_OFFSET,   
	XLLP_MFP_L_DD_2_OFFSET,     
	XLLP_MFP_L_DD_3_OFFSET,      
	XLLP_MFP_L_DD_4_OFFSET,      
	XLLP_MFP_L_DD_5_OFFSET,       
	XLLP_MFP_L_DD_6_OFFSET,       
	XLLP_MFP_L_DD_7_OFFSET,      
	XLLP_MFP_L_FCLK_OFFSET,       
	XLLP_MFP_L_LCLK_OFFSET,         
	XLLP_MFP_L_PCLK_OFFSET,       
	XLLP_MFP_L_BIAS_OFFSET,
		       
	XLLP_MFP_PIN_EOLIST_MARKER 
};

static XLLP_UINT32_T gpio_list[] = {
	63,
	64,
	65,
	66,
	67,
	68,
	69,
	70,
	71,
	72,
	6,
	7,
	8,
	9,
	10,
	11,
	12,
	13,
	14,
	15,
	16,
	17
};


static XLLP_MFP_ALT_FN_T af_list[] = {
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0,
	XLLP_MFP_ALT_FN_0
};

static XLLP_MFP_DRIVE_STRENGTH_T drive_strength_list[] = {
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X,
	XLLP_MFP_DS_08X
};

